home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / latex209 / contrib / chbars / chbars.sty (.txt) < prev    next >
LaTeX Document  |  1993-01-11  |  39KB  |  801 lines

  1. % This is CHBARS.STY                      as of Aug '92
  2. % -*-LaTeX-*-
  3. %---------------------------------------------------------
  4. % (c) 1989 by J.Schrod. copy conditions see below.
  5. % Macro package for creating changebars in LaTeX.
  6. % MAKEPROG will ``weave'' this file into documentation that can be LaTeX'ed.
  7. % documented in LaTeX (for Anne and Chris)
  8. %       VERSION HISTORY  (MSCF -- most significant change first)
  9. % DATE     PERSON  REMARK
  10. % 92-08-15 -rlb    Use change bars in this document to mark major changes;
  11. %                  so now you can see for yourself what they are.
  12. % 92-07-28 -rlb    Run through a spelling checker before distributing.
  13. % 92-01-15 -rlb    1) Keep \maxdeadcycles the same; just 
  14. %                     don't count calls to output for change processing. 
  15. %                  2) Allow setting the change bar width.
  16. %                  3) Some typos and rewording of some of the comments.
  17. %                  4) Allow setting change bar width. Interface copied
  18. %                     from changebars.sty
  19. %                  5) Allow changebars to be on the left as well as
  20. %                     the right.
  21. %                  6) localize some variables global definitions now
  22. %                     start with cb_.
  23. %                  7) Chain onto existing \output routine rather than assume
  24. %                     \plainoutput.
  25. %                  And miscellaneous minor hacks.
  26. % 89-10-09 -js     converted to LaTeX (progltx)
  27. % 89-09-25 -js     repaired \mark processing in horizontal mode
  28. % 89-08    -js     first version (for EuroTeX89 in Karlsruhe)
  29. % author's current address:
  30. %       Detig$\,\cdot\,$Schrod \TeX{}sys
  31. %       Joachim Schrod
  32. %       Kranichweg 1
  33. %       D-6074 R\"odermark-Urberach
  34. %       FR Germany
  35. %       Tel. (+6074) 1617
  36. %       Bitnet: XITIJSCH@DDATHD21
  37. % should be progtex...
  38. %%%% These TeX macros were documented with the documentation system
  39. %%%% MAKEPROG and automatically converted to the current form.
  40. %%%% If you have MAKEPROG available you may transform it back to
  41. %%%% the original input: Remove every occurence of three percents
  42. %%%% and one optional blank from the beginning of a line and remove
  43. %%%% every line which starts with four percents.  The following lex
  44. %%%% program will do this:
  45. %%%%    %%
  46. %%%%    ^%%%\ ?   ;
  47. %%%%    ^%%%%.*\n ;
  48. %%%% MAKEPROG may be obtained over the net from the Bitnet-Listserver
  49. %%%% LISTSERV@DHDURZ1 (filelist WEBWARE), from tuglib@science.utah.edu,
  50. %%%% or via ftp from june.cs.washington.edu.
  51. %%% \documentstyle[progltx,chbars,a4-9]{article}
  52. %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  53. %%% %
  54. %%% % local macros
  55. %%% %
  56. %%% \let\mc=\small              % for names like GNU
  57. %%% \def\PS{{\sc PostScript}}
  58. %%% \def\DVI{{\tt DVI}}
  59. %%% \def\GNU{{\mc GNU}}
  60. %%% \chardef\bs=`\\
  61. %%% %
  62. %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  63. %%% \begin{document}
  64. %%% \tableofcontents
  65. %%% \title{Changebars without {\tt \bs{}special}'s}
  66. %%% \author{\sc Joachim Schrod}
  67. %%% \newcommand{\changedate}{Aug 15, 1992}
  68. %%% \date{Revised last on \changedate\footnote{by {\tt rocky@watson.ibm.com}}\\ 
  69. %%%       Formatted on \today}
  70. %%% \maketitle
  71. %%% \begin{abstract}
  72. %%% It is common practice to use vertical bars in the margins of a
  73. %%% document to mark pieces of text which have changed since the last
  74. %%% version(s) of this document. Such vertical bars are usually
  75. %%% called {\em changebars}. It has often been said that it is
  76. %%% impossible to produce changebars with \TeX{} without the usage of
  77. %%% |\special| commands (driver directives), which extend the
  78. %%% primitives of \TeX{}. This paper presents a \TeX{} macro file
  79. %%% which implements changebars without such a usage. The macro file
  80. %%% is written for the usage with {\sc Plain}~\TeX{} but the implementation 
  81. %%% strategy can be used with \LaTeX{}, too with minor changes.
  82. %%% \end{abstract}
  83. %%% \chap Introduction.
  84. %%% Changebars are used to mark modified parts in existing documents.  For
  85. %%% the usage in \TeX{} documents, there exist only solutions that use
  86. %%% driver/printer features by the way of inserting |\special| commands in
  87. %%% the \TeX{} source, e.g.\ for \PS{} drivers.  This results in documents
  88. %%% that are no longer as freely interchangeable as the \DVI{} concept would
  89. %%% allow---device dependency is problematic especially for this application
  90. %%% that is useful for multi-authoring or standards development.
  91. %%% This macro package offers a pure \TeX{} solution.  Nevertheless, it has
  92. %%% its restrictions, too. The page break will no longer be optimal,
  93. %%% because there is no strechability or shrinkability of a page 
  94. %%% on top of the last
  95. %%% region of change marked. But this seems to be acceptable,
  96. %%% especially as the change bar feature often will be used for proof
  97. %%% reading and not in the final document. This restriction is the reason
  98. %%% why no change marks can be used on title pages or on similar
  99. %%% constructions. Changes in floating insertions (footnotes, figures) are
  100. %%% not handled. Multi-column formats such as a two-column layout
  101. %%% will not work. There is currently no support for nested changes.
  102. %%% The method for writing a change bar consists of three
  103. %%% parts: First, the output routine is signalled when the beginning
  104. %%% or end of a change bar setting command is encountered.
  105. %%% Next, the position of the changed area is found out
  106. %%% and fixed; the end-of-change command adds the last change bar
  107. %%% position, length and width to a list of all such positions,
  108. %%% lengths and widths of change bars that is accumulated for the current 
  109. %%% page. Finally, when
  110. %%% the output routine is triggered (either asynchronously in trying
  111. %%% to ship out the page or synchronously when discovering that the
  112. %%% end of a change bar lies off the page), this list of change
  113. %%% bars information is used create vertical rules which are added to the page.
  114. %%% If the change bar is not complete, a ``virtual'' end change is
  115. %%% inserted, and a ``virtual'' begin change is inserted at the
  116. %%% beginning of the next page.
  117. %%% \beginchange
  118. %%% The demonstrated solution was originally written in {\sc Plain}
  119. %%% \TeX{}, because it was
  120. %%% easier and could be presented better at the Euro\TeX89 conference in
  121. %%% Karlsruhe.  An adaptation to \LaTeX{} has been done too which
  122. %%% requires minor modifications to the \LaTeX's output routine.
  123. %%% \endchange
  124. %%% Some history. The routines were initially written by Joachim
  125. %%% Schrod. Around Jan.\ 1992, R.\ Bernstein added some of the features
  126. %%% coded in |changebars.sty| to combine the best features
  127. %%% of the two (and added a couple of his own). For example, the
  128. %%% ability to specify change bar widths, put the change bars either on the
  129. %%% right or left margin, specifiy the distance from the margin to the
  130. %%% change bar, and chain to on top of a pre-existing modified 
  131. %%% output routine. 
  132. %%% \beginchange
  133. %%% Initially, both the {\TeX} and the {\LaTeX}
  134. %%% versions were put into one file. However, due problems in
  135. %%% dealing with conditional definition of code, in particular
  136. %%% problems with an extra or omitted |\fi| in defining or not
  137. %%% |\ifr@ggedbottom|, the code was split into two. 
  138. %%% \endchange
  139. %%% The |changebars.sty| package
  140. %%% was written by Michael Fine and revised by Johannes Braams
  141. %%% and Neil Winton. One or two ideas from Thomas J.~Reid have been
  142. %%% used.
  143. %%% \chap GNU General Public License.
  144. %%% This program is free software; you can redistribute it and/or
  145. %%% modify it under the terms of the \GNU{} General Public License as
  146. %%% published by the Free Software Foundation; either version~2, or (at your
  147. %%% option) any later version.
  148. %%% This program is distributed in the hope that it will be useful, but
  149. %%% {\bf without any warranty\/}; without even the implied warranty of
  150. %%% {\bf merchantability\/} or {\bf fitness for a particular purpose}.  See
  151. %%% the \GNU{} General Public License for more details.
  152. %%% You should have received a copy of the \GNU{} General Public License
  153. %%% along with this program; if not, write to the Free Software Foundation,
  154. %%% Inc., 675~Mass Ave, Cambridge, MA~02139, USA.
  155. %%% \chap User Interface.
  156. %%% \begin{newsloppy}
  157. %%% A changed area is described by two marks, |\beginchange| and
  158. %%% |\endchange|. The |\beginchange| can take an optional argument,
  159. %%% the bar thickness, enclosed in brackets. That is, either of the
  160. %%% something like |\beginchange[0.4pt]|, or |\beginchange| is valid. 
  161. %%% In the latter case where no thickness dimension is specified, the value
  162. %%% of |\changebarwidth| is used. Therefore, by setting this you can
  163. %%% change the bar thickness used when none is given. Changing the bar
  164. %%% thickness might be useful in identifying different classes of changes.
  165. %%% Another pair of global values that can be set are the 
  166. %%% logical variables 
  167. %%% {\tt \bs chbar\-Right\-true} and {\tt \bs chbar\-Right\-false}. 
  168. %%% These specify whether
  169. %%% changebars should be on the right or left. Say, if one wanted to
  170. %%% have the changebars always appear on the outside margin, one could
  171. %%% change these accordingly on each page.
  172. %%% \end{newsloppy}
  173. %%% Finally |\BarDistance| is the amount of place between the text margin
  174. %%% and the change bars. This value should normally be positive. For
  175. %%% change bars on the right this value is added to |\hsize|, while
  176. %%% for change bars on the left this value is {\em subtracted\/} from
  177. %%% |\hoffset|.
  178. %%% \beginprog
  179. \newdimen\changebarwidth \changebarwidth=1pt\relax
  180. \newif\ifchbarRight \chbarRightfalse
  181. \newdimen\BarDistance  \BarDistance=2cc
  182. %%% \endprog
  183. %%% \chap Utility Routines and Programming Conventions.
  184. %%% Before we get into the nitty gritty details, we give some common
  185. %%% macros. 
  186. %%% First, we declare some shorthands for category codes.  By
  187. %%% declaring the at sign~(`|@|') as well as the underscore~`(|_|)' as
  188. %%% letters we can use them in our macros. (I agree with D.~Knuth that
  189. %%% |\identifier_several_words_long| is more readable than
  190. %%% |\IdentifierSeveralWordsLong| and in every case better than |\p@@@s|.)
  191. %%% \beginchange
  192. %%% By defining the at sign to be in the letter class,
  193. %%% we can access {\sc Plain} \TeX's ``private''macros. By defining the
  194. %%% underscore to be in the letter class, we make our own private macros more 
  195. %%% readable. But since have to
  196. %%% restore these category codes at the end of this macro file, we store
  197. %%% \endchange
  198. %%% their former values in the control sequences |\atcode| and |\uscode|.
  199. %%% This method is better than to use a group because not all macros have to
  200. %%% be defined global this way.
  201. %%% All {\em global\/} definitions of the package are prefixed by
  202. %%% |cb_|. In this way, they can be easily determined. If other
  203. %%% packages do likewise, it is less likely that definition
  204. %%% names will clash between packages. (Unless the other package uses the
  205. %%% |cb_| prefix too, in which case there is a good chance that many
  206. %%% variable names will clash.)
  207. %%% The {\TeX}book recommends the 255 registers be used for scratch
  208. %%% space, so routines use this value when a spare register is
  209. %%% needed. (See for example |\cb_write_bar|.)
  210. %%% \beginprog
  211. \chardef\letter=11
  212. \chardef\atcode=\catcode`\@
  213. \chardef\uscode=\catcode`\_
  214. \catcode`\@=\letter
  215. \catcode`\_=\letter
  216. %%% \endprog
  217. %%% \sect Now we are ready to code the top-level routine for indicating the 
  218. %%% beginning of a change. But first we display the banner and
  219. %%% version number associated with this package. If this package has
  220. %%% been loaded already we terminate.
  221. %%% Although one can specify a bar
  222. %%% thickness on the |\beginchange| macro, it is at the |\endchange|
  223. %%% where this thickness is recorded and put on a current list of
  224. %%% change bar entries for the current page. It seems more natural to 
  225. %%% specify the thickness at the beginning of the change rather than
  226. %%% the end. Therefore at the
  227. %%% |\beginchange| we merely save the value given or save the value of
  228. %%% |\changebarwidth| if no thickness was given.
  229. %%% |\cb_changebarwidth| is this saved value. Auxiliary routine
  230. %%% |\cb_xstart| is used to parse off the optional enclosing
  231. %%% brackets. (This weird name was taken from one used in |changebars.sty|.)
  232. %%% default value, that is the value when a width is not specified 
  233. %%% is |\changebarwidth|.
  234. %%% \beginprog
  235. \@ifundefined{cb_xstart}{}{\endinput}
  236. \typeout{Style option `change bars' -- version 1.1a}
  237. \newdimen\cb_changebarwidth
  238. \def\beginchange{\@ifnextchar [
  239.                     {\cb_xstart}
  240.                     {\beginchange_\changebarwidth}}
  241. \def\cb_xstart[#1]{\beginchange_{#1}}
  242. %%% \endprog
  243. %%% \chap Triggering the Output Routine.
  244. %%% \beginchange
  245. %%% When a change bar is started or completed, we signal the output
  246. %%% routine in the ``usual'' way described below. Why bother the
  247. %%% output routine? The output routine is supposed to be thought of 
  248. %%% as an asynchronous process which has access to the page via the
  249. %%% contents of box register 255. In theory, if we did not perform
  250. %%% actions in the output routine, it might get triggered inadvertantly,
  251. %%% and the output routine might modify this box register on us in the
  252. %%% midst of our work. The information that needs to be recorded
  253. %%% is the position of the beginning and end of the lines containing a
  254. %%% marked change region.
  255. %%% \begin{newsloppy}
  256. %%% The macros |\beginchange| and |\endchange| signal the output
  257. %%% routine. Rather than merely calling the output routine directly,
  258. %%% these routines signal the output routine by setting a low page penalty, i.e.,
  259. %%% one which tells {\TeX} that this is a really good place to break
  260. %%% the page. Of course, we will modify the output routine so that it
  261. %%% doesn't really split the page when it has been called in this fashion.
  262. %%% Again, the reason we signal the output routine in what seems at
  263. %%% first a pretty odd way, is that this is the way it is supposed to
  264. %%% be done. If a low penalty were not set, some 
  265. %%% other action (if we weren't careful) might cause a penalty to be 
  266. %%% set and thus call the output routine (recursively).
  267. %%% So that we can distinguish our calls from others, |\beginchange|
  268. %%% and |\endchange| reserve a range of 
  269. %%% penalty values; he actual value is stored in |\cb_break_penalty|.
  270. %%% This range of values is below 
  271. %%% $-10\,000$, the nominal value for indicating that a page should
  272. %%% occur now. The penalty will be in the range $|\cb_penalty_group|
  273. %%% \cdot 100 - 99 \ldots |\cb_penalty_group| \cdot 100$.
  274. %%% The output routine then decides whether it has been called 
  275. %%% to start or end a change bar or neither.  This is done with the values
  276. %%% |\cb_penalty_begin| and |\cb_penalty_end| that are used as the
  277. %%% second-to-last digit of the change penalty.
  278. %%% \end{newsloppy}
  279. %%% The output routine also needs to determine whether
  280. %%% the beginning of change bar starts in horizontal or vertical mode
  281. %%% so it can figure out the exact placement for the beginning of the
  282. %%% bar line. In horizontal mode, the change bar does
  283. %%% not begin at the baseline of the actual text position, but on top of the
  284. %%% actual line.  This is marked in the last digit, an odd digit will be
  285. %%% used in horizontal mode.
  286. %%% Note that the values mentioned above are used as digits here that can be
  287. %%% concatenated.  If they are not followed by an other digit they should be
  288. %%% terminated by |\space| to stop {\TeX}'s look-ahead scanning for
  289. %%% digits when reading a number. 
  290. %%% \endchange
  291. %%% \beginprog
  292. \def\cb_penalty_group{-101} \def\cb_penalty_begin{0}
  293. \def\cb_penalty_end{1}
  294. %%% \endprog
  295. %%% \sect The calls to start or end a change bar, set an encoded page-break
  296. %%% penalty which includes an
  297. %%% indication of begin or end of a change bar. The variable
  298. %%% |\cb_break_penalty| is used to create such as special value.
  299. %%% The rest (|\cb_trigger_output|) is the same action
  300. %%% for both. The region just before the end of the changed region can be 
  301. %%% in horizontal mode and preceded by
  302. %%% glue that could cause a line break, thus including the following line to
  303. %%% the change area as well.  To avoid this unwanted behavior, the space is
  304. %%% saved in |\save_lastskip|, discarded in front of the mark and restored
  305. %%% afterwards. 
  306. %%% \beginprog
  307. \newcount\cb_break_penalty
  308. \def\beginchange_#1{%
  309.    \cb_changebarwidth=#1
  310.    \cb_break_penalty=\cb_penalty_group\cb_penalty_begin0
  311.    \cb_trigger_output
  312. \def\endchange{{%
  313.    \skipdef\save_lastskip=255%
  314.    \ifhmode \save_lastskip=\lastskip \unskip \fi%
  315.    \cb_break_penalty=\cb_penalty_group\cb_penalty_end0%
  316.    \cb_trigger_output%
  317.    \ifhmode \hskip\save_lastskip \fi%
  318.    }}%\endchange
  319. %%% \endprog
  320. %%% \sect The next routine, |\cb_trigger_output|,
  321. %%% triggers calls to the output routine by setting the page-break 
  322. %%% penalty, |\penalty|. {\TeX} may discard an |\output| invocation at
  323. %%% the beginning of a page.  So we trigger the output routine twice, first
  324. %%% with a special penalty value that is 2~less than the correct value
  325. %%% (including the code for horizontal or vertical mode).  After the first
  326. %%% page break, it is asserted that the current list is empty.  The output
  327. %%% routine has to save the former page contents if necessary.
  328. %%% Next we set the penalty to the correct value.  The second page break does
  329. %%% the real work, restores the page contents and handles the split
  330. %%% insertions (footnotes, figures,~\dots). 
  331. %%% In horizontal mode |\spacefactor| must not be destroyed, so it is
  332. %%% saved and restored via local count register |\save_spacefactor|.
  333. %%% \beginprog
  334. \def\cb_trigger_output{%
  335.    \ifinner \errmessage{Change cannot be marked inside a box}%
  336.    \else{%
  337.      \countdef\save_spacefactor=255%
  338.      \ifvmode   
  339.        \let\do_in_vmode=\relax
  340.        \advance \cb_break_penalty by -2
  341.      \else 
  342.        \save_spacefactor=\spacefactor
  343.        \let\do_in_vmode=\vadjust
  344.        \advance \cb_break_penalty by -3
  345.      \fi
  346.      \do_in_vmode{%
  347.        \penalty\cb_break_penalty%        first call to \output
  348.        \null
  349.        \advance \cb_break_penalty by 2 
  350.        \penalty\cb_break_penalty%        second call to \output
  351.        }%
  352.      \ifhmode \spacefactor=\save_spacefactor \fi
  353.      }%
  354.    \fi
  355.    }% cb_trigger_output
  356. %%% \endprog
  357. %%% \sect Using the output routine for passing
  358. %%% information has it's difficulties.  One of the hard parts is 
  359. %%% \beginchange
  360. %%% handling page marks. These are token lists which are set by the
  361. %%% |\mark| command. They record information that can later 
  362. %%% be accessed by the output routine. The canonical example of such
  363. %%% a use of the |\mark| command is in creating 
  364. %%% \endchange
  365. %%% dictionary-style entry headings.
  366. %%% The output routine can access one of three 
  367. %%% token lists through three control sequences:  |\botmark| is the
  368. %%% last page mark given, |\topmark| is the |\botmark| of the previous
  369. %%% page, and |\firstmark| is the first page mark on the actual page or
  370. %%% |\topmark| if none was given.  Here ``page'' is used in the \TeX{}
  371. %%% sense, i.e.\ as the material which has been collected between two
  372. %%% |\output| invocations. Of course, the page marks must not be
  373. %%% destroyed---and that means they must be reinserted after each
  374. %%% special use of the output routine.
  375. %%% But we are lucky: A ``special use'' consists of two |\output|
  376. %%% invocations, so we can insert |\topmark| again as a page mark after
  377. %%% the first invocation where it will be the only page mark on that
  378. %%% \TeX{} page.  The second invocation will automatically transform this
  379. %%% page mark into the ``last page mark on the previous page,'' i.e.\ in
  380. %%% |\topmark|---that's what we need!  Furthermore |\firstmark| and
  381. %%% |\botmark| are saved in control sequences during the first invocation,
  382. %%% they will be inserted again, too.
  383. %%% There's one situation where this approach doesn't work: in front of
  384. %%% the first page mark. Here, |\topmark|, |\firstmark|, and |\botmark| expand
  385. %%% to an empty token list. If we save them then and insert their old
  386. %%% values we have inserted empty page marks. If other page marks follow
  387. %%% on the same ``real'' page, |\firstmark| will be empty instead of
  388. %%% expanding to the token list of the first page mark.  To prevent this
  389. %%% we must not save and restore page marks before the first |\mark| has
  390. %%% been added to the main vertical list.
  391. %%% Well, that can be controlled with a switch---but this switch must be
  392. %%% set very carefully.  If it is set immediately by the first |\mark|
  393. %%% this may be in horizontal mode and special output invocations can
  394. %%% occur above this page mark (i.e., there may be a |\beginchange| in the
  395. %%% same paragraph in front of the |\mark|).  Therefore the setting of the
  396. %%% switch must be delayed until the vertical position of the |\mark|
  397. %%% (precisely:  the position of the |\mark| in the current list) is
  398. %%% reached.  In horizontal mode this can be done with a |\vadjust| and
  399. %%% the output routine! Voil\`a, this is another command group for the
  400. %%% output routine with only one command.
  401. %%% \beginprog
  402. \newif\if_cb_save_mark@  \_cb_save_mark@false
  403. \def\mark_penalty_group{-102}
  404. %%% \endprog
  405. %%% \sect We will redefine |\mark| so that the first page mark either sets
  406. %%% the switch to true (in vertical mode all possible special page breaks
  407. %%% are already handled) or forces the |\output| routine to do this at an
  408. %%% appropriate place. In the last case we can use |\cb_trigger_output|
  409. %%% again. Afterwards we restore the original meaning of |\mark| again to
  410. %%% reduce the processing overhead (and the dead cycles).
  411. %%% This change of |\mark| has the consequence that the first |\mark| in a
  412. %%% document cannot be used anymore in horizontal mode inside a vertical
  413. %%% box that shall be split afterwards.  But this is only sensible if this
  414. %%% mark shall be used as |\splitfirstmark| because it will almost never
  415. %%% migrate to the outer list---really a rare case!
  416. %%% \beginprog
  417. \let\cb_mark=\mark
  418. \def\mark{%
  419.    \ifvmode
  420.      \ifinner \else  \global\_cb_save_mark@true \fi % split marks!
  421.    \else  
  422.      \cb_break_penalty=\mark_penalty_group00 % this will corrupt \vsplit
  423.      \cb_trigger_output
  424.    \fi
  425.    \global\let\mark=\cb_mark
  426.    \cb_mark
  427. %%% \endprog
  428. %%% \sect 
  429. %%% \begin{newsloppy}
  430. %%% If the output routine is triggered with the mark penalty value,
  431. %%% it will call {\tt \bs cb\_save\discretionary{\_}{}{}page\_marks}.
  432. %%% \end{newsloppy}
  433. %%% \beginprog
  434. \def\cb_save_page_marks{%            % this may be executed twice
  435.    \unvbox\@cclv
  436.    \global\_cb_save_mark@true
  437. %%% \endprog
  438. %%% \sect To finish the treatment of page marks we can formulate the two
  439. %%% macros which are used at the first resp.\ second invocation of a
  440. %%% ``special output,'' the principles have already been explained.
  441. %%% \beginprog
  442. \def\cb_backup_page_marks{%
  443.    \if_cb_save_mark@
  444.       \mark{\topmark}%
  445.       \xdef\cb_save_firstmark{\firstmark}%
  446.       \xdef\cb_save_botmark{\botmark}%
  447.    \fi
  448. \def\cb_restore_page_marks{%
  449.    \if_cb_save_mark@
  450.       \mark{\cb_save_firstmark}\mark{\cb_save_botmark}%
  451.    \fi
  452. %%% \endprog
  453. %%% \chap Positioning the Change Bars.
  454. %%% \begin{newsloppy}
  455. %%% Now we handle the positions of the bars.  
  456. %%% The dimension |\cb_bot_change_pos| will hold
  457. %%% the position of the end of the change bar, i.e.\ the distance between 
  458. %%% top of page the end of the changed area. The dimension
  459. %%% |\cb_top_change_pos| will hold the beginning of a changed
  460. %%% area; a value of |\maxdimen| indicates that no change is in effect.  If
  461. %%% a changed area is completed, it is appended to the list |\cb_bar_list| as
  462. %%% an element {\tt \bs cb\_bar(\bs cb\_top\_change\_pos,
  463. %%% \bs cb\_bot\_change\_pos, \bs changebar\-width)}.  This list contains all
  464. %%% changed areas within the current page so that bars can be written later
  465. %%% on.  A single bar will be produced by |\cb_write_bar|.
  466. %%% \noindent The definition of |\cb_bar| to |\relax| allows the concatenation
  467. %%% of new elements to |\cb_bar_list| with |\xdef|.
  468. %%% Local dimension register |\halfwidth| is used to center the change bar.
  469. %%% \end{newsloppy}
  470. %%% \beginprog
  471. \newdimen\cb_bot_change_pos
  472. \newdimen\cb_top_change_pos  \cb_top_change_pos=\maxdimen
  473. \let\cb_bar_list=\empty
  474. \let\cb_bar=\relax
  475. \def\cb_write_bar(#1,#2,#3){{%
  476.    \dimendef\halfwidth=255%
  477.    \setbox0=\hbox{\vrule width #3 height -#1  depth #2}%
  478.    \dp0=0pt \ht0=0pt \wd0=0pt%
  479.    \halfwidth=#3 \divide\dimen255 by 2%
  480.    \hskip -\halfwidth%
  481.    \box0%
  482.    \hskip \halfwidth%
  483.    }}
  484. %%% \endprog
  485. %%% \sect If the output routine was activated by a |\outputpenalty| value
  486. %%% within the range of our reserved penalties, the change handling will
  487. %%% occur, otherwise standard plain output can be done. There may be
  488. %%% a lot of interaction between the routines which set change bars and
  489. %%% the output routine. These interactions should not be recorded in
  490. %%% |\deadcycles| or else \TeX{} will soon grumble. One might consider
  491. %%% doing the same for the|\mark_penalty_group|. But since this 
  492. %%% group is called once it shouldn't matter all that much.
  493. %%% One might also consider adding a counter like |\deadcycles| just
  494. %%% to count the change bar interactions as is done for the calls to
  495. %%% |\output|, and have {\TeX} grumble if there are ``too many'' of
  496. %%% them. For now we don't do this---all of this code is correct
  497. %%% anyway!
  498. %%% First we save away the old output routine in case the user had redefined
  499. %%% this beforehand.
  500. %%% \beginprog
  501. \newtoks\cb_oldoutput
  502. \edef\cb_oldoutput{\the\output}
  503. \newcount\penalty_group
  504. \output={%
  505.    \boxmaxdepth=\maxdepth
  506.    \penalty_group=\outputpenalty  \divide \penalty_group by 100
  507.    \ifnum \penalty_group=\cb_penalty_group\space  
  508.      {\countdef\new_deadcycles=255% don't count as a dead cycle
  509.       \new_deadcycles=\deadcycles       
  510.       \advance \new_deadcycles by -1
  511.       \deadcycles=\new_deadcycles}
  512.      \cb_change_handling
  513.    \else
  514.       \ifnum \penalty_group=\mark_penalty_group\space
  515.          \cb_save_page_marks
  516.       \else \cb_oldoutput
  517.       \fi
  518.    \fi
  519. %%% \endprog
  520. %%% \sect As explained before, the change handling must differentiate
  521. %%% between the kind of the change command (beginning is indicated by
  522. %%% $|\cb_change_cmd|=0$, end by~1) and between the mode (horizontal indicated
  523. %%% by an odd |\cb_change_mode| value, vertical by an even).  A change mode
  524. %%% higher than one indicates that we are doing the first page break that
  525. %%% has to backup the page as far as it exists already and results in an
  526. %%% empty current list of page elements.
  527. %%% \beginprog
  528. \newcount\cb_change_cmd
  529. \newcount\cb_change_mode
  530. \def\cb_change_handling{%
  531.    \cb_change_cmd=-\outputpenalty                  % ==> absolute value
  532.    \advance \cb_change_cmd by \cb_penalty_group00  % subtraction
  533.    \cb_change_mode=\cb_change_cmd
  534.    \divide \cb_change_cmd by 10                    % second-to-last digit
  535.    \advance \cb_change_mode by -\number\cb_change_cmd0   % last digit
  536.    \ifnum \cb_change_mode>1  \cb_backup_page
  537.    \else
  538.       \ifcase \cb_change_cmd  \cb_begin_change
  539.       \or \cb_end_change
  540.       \else \errmessage{Invalid changepenalty}%
  541.       \fi
  542.    \fi
  543. %%% \endprog
  544. %%% \sect Processing a mark during the second trigger of the output routine
  545. %%% means restoring the page and storing the positions.  At the beginning,
  546. %%% the begin of the change is saved, at the end, we know the bar already
  547. %%% and put it into the bar list.  Then the positioning values are
  548. %%% reinitialized.
  549. %%% As within every output invocation, the box 255 must be unboxed.  As we
  550. %%% are here in the second invocation of the output routine the |\box255|
  551. %%% consists only of the empty |\vbox| we have inserted in
  552. %%% |\cb_trigger_output|.  We can therefore throw it away.
  553. %%% \beginprog
  554. \def\cb_begin_change{%
  555.    \cb_restore_page
  556.    \setbox0=\box\@cclv
  557.    \ifdim \cb_top_change_pos=\maxdimen
  558.       \global\cb_top_change_pos=\cb_bot_change_pos  
  559.       \global\cb_bot_change_pos=0pt
  560.    \else \errmessage{Nested change bars are not supported}%
  561.    \fi
  562. \def\cb_end_change{%
  563.    \cb_restore_page
  564.    \setbox0=\box\@cclv
  565.    \ifdim \cb_top_change_pos=\maxdimen
  566.       \errmessage{No change is in effect}%
  567.    \else
  568.       \xdef\cb_bar_list{\cb_bar_list 
  569.          \cb_bar(\the\cb_top_change_pos,\the\cb_bot_change_pos,
  570.                  \the\cb_changebarwidth)}
  571.       \global\cb_top_change_pos=\maxdimen
  572.       \global\cb_bot_change_pos=0pt
  573.    \fi
  574. %%% \endprog
  575. %%% \chap Handling the Page Contents.
  576. %%% We handle the part of the page that was collected up to now by putting
  577. %%% it into a box. This fixes the position of the change mark so that
  578. %%% |\cb_bot_change_pos| can be set and stored in |\cb_top_change_pos|
  579. %%% later on or as the lower end of a bar in |\cb_bar_list|.
  580. %%% In the first output invocation, we save the contents of the 
  581. %%% page in |\cb_save_page|. Before that, we store the size of the
  582. %%% box (which equals |\pagegoal|!)\ in |\cb_page_goal|.  If the unboxing
  583. %%% caused an increase of height (i.e.\ if $|\pagetotal|>|\pagegoal|$),
  584. %%% we eject the page up to the change mark.  Now we have to compute the
  585. %%% current position of our mark in |\cb_bot_change_pos|. It is fixed by the size
  586. %%% of the |\cb_save_page|, but if the change begins in horizontal mode we
  587. %%% must decrease it from the baseline position to the top of the last line.
  588. %%% Finally, we must save the values for the allowed insertions and change
  589. %%% them to the maximal value so that a rest that is split from an insertion
  590. %%% will be appended to the insertion box at the second invocation in every
  591. %%% case.
  592. %%% The |\vsize| is initialized to |\maxdimen|.  This allows to control
  593. %%% whether this first output invocation ocurred or if it was discarded.
  594. %%% For the same reason |\cb_bot_change_pos| is initialized to~0pt.
  595. %%% \beginprog
  596. \newbox\cb_save_page
  597. \newdimen\cb_page_goal
  598. \newdimen\cb_save_vsize         \cb_save_vsize=\maxdimen
  599. \newdimen\cb_save_dimen_topins
  600. \newdimen\cb_save_dimen_footins
  601. \def\cb_backup_page{%
  602.    \global\cb_page_goal=\ht\@cclv
  603.    \global\setbox\cb_save_page=\vbox{\unvbox\@cclv}%
  604.    \ifdim \ht\cb_save_page>\cb_page_goal  
  605.       \cb_eject_page_so_far \fi
  606.    \cb_bot_change_pos=\ht\cb_save_page  
  607.    \global\advance \cb_bot_change_pos by \dp\cb_save_page
  608.    \ifnum \cb_change_cmd=\cb_penalty_begin\space
  609.       \ifodd \cb_change_mode \higher_change_pos \fi
  610.    \fi
  611.    \global\cb_save_vsize=\vsize  \global\vsize=\maxdimen
  612.    \global\cb_save_dimen_footins=\dimen\footins
  613.    \global\dimen\footins=\maxdimen
  614.    \cb_backup_page_marks
  615. \cb_bot_change_pos=0pt
  616. %%% \endprog
  617. %%% \sect To eject a page as far as it is we restore it from the
  618. %%% |\cb_save_page| back to box~255.  In horizontal mode and at a begin mark
  619. %%% the last line contains the mark and must not be output.  So we remove it
  620. %%% and the preceding glue from the stored rest, just leaving a single hbox
  621. %%% to be on top of the actual page (in |\cb_save_page|) now. Then normal
  622. %%% output can be done with box~255.
  623. %%% \beginprog
  624. \def\cb_eject_page_so_far{%
  625.    \begingroup
  626.      \vbadness=20000 % don't complain about underfull vboxes
  627.      \global\setbox\@cclv=\vbox to \cb_page_goal{%
  628.          \unvbox\cb_save_page
  629.          \ifnum \cb_change_cmd=\cb_penalty_begin\space
  630.             \ifodd \cb_change_mode
  631.                \global\setbox\cb_save_page=\lastbox
  632.                \unskip
  633.             \fi
  634.          \fi
  635.          }%
  636.    \endgroup
  637.    \cb_oldoutput
  638. %%% \endprog
  639. %%% \sect 
  640. %%% \begin{newsloppy}
  641. %%% In horizontal mode and at a begin mark, we need the position of
  642. %%% the mark ({\tt \bs cb\_bot\discretionary{\_}{}{}change\_pos}) on the upper boundary of 
  643. %%% the last line in
  644. %%% |\cb_save_page|.  If there is just one line left from a recent eject, the
  645. %%% height is given by |\topskip| decreased by the height of this hbox.  If
  646. %%% the height of the box is larger than |\topskip| the skip will not be
  647. %%% inserted and the change position results to~0pt. Otherwise,
  648. %%% |\cb_save_page| is a vbox whose last hbox we delete temporarily using
  649. %%% box~0. Height and depth of the rest are the actual position on the page.
  650. %%% \end{newsloppy}
  651. %%% The double of the page we have constructed this way will immediately be
  652. %%% fed back to the garbage collector because it could have become
  653. %%% reasonably large.
  654. %%% \beginprog
  655. \def\higher_change_pos{%
  656.    \ifhbox \cb_save_page % rest of page from \cb_eject_page_so_far
  657.       \cb_bot_change_pos=\topskip  
  658.       \global\advance \cb_bot_change_pos by -\ht\cb_save_page
  659.       \ifdim \cb_bot_change_pos<0pt  
  660.          \global\cb_bot_change_pos=0pt  
  661.       \fi
  662.    \else
  663.       \setbox0=\vbox{%
  664.          \unvcopy\cb_save_page
  665.          \setbox0=\lastbox % delete last line
  666.          }%
  667.       \cb_bot_change_pos=\ht0   
  668.       \global\advance \cb_bot_change_pos by \dp0
  669.       \setbox0=\box\voidb@x
  670.    \fi
  671. %%% \endprog
  672. %%% \sect To restore a page during the second output invocation, we first
  673. %%% restore the saved values, but only if they were really changed (this can
  674. %%% be discovered by the value of |\cb_save_vsize|).  Now the |\cb_save_page| is
  675. %%% appended to the current list as a box, which stops later usage of its
  676. %%% stretch- and shrinkability!  Then the collected insertions can be
  677. %%% inserted again. The page marks have to be inserted, too.
  678. %%% \beginprog
  679. \def\cb_restore_page{%
  680.    \ifdim \cb_save_vsize=\maxdimen
  681.    \else  \global\vsize=\cb_save_vsize
  682.      \global\dimen\footins=\cb_save_dimen_footins
  683.      \global\cb_save_vsize=\maxdimen
  684.      \cb_restore_page_marks
  685.    \fi
  686.    \box\cb_save_page % discards stretch- and shrinkability!
  687.    \ifvoid \footins
  688.    \else  \insert\footins{\floatingpenalty=20000 \unvbox\footins}%
  689.    \fi
  690. %%% \endprog
  691. %%% \sect {\em Please note, that there is still a problem with this concept 
  692. %%% of handling the output trigger:}
  693. %%% \bigskip
  694. %%% If the first output trigger is discarded because a page break has occurred 
  695. %%% just in front, footnote parts may be juggled around. I.e., if a 
  696. %%% footnote is split in three parts, the first part was just been shipped 
  697. %%% out, the second part is inserted back into the recent contributions by 
  698. %%% the output routine but {\em behind\/} the third part which is saved in 
  699. %%% the ``special place'' (according to the \TeX{}book, p.~125). A solution to
  700. %%% this problem might be to insert a |\do_change| again within the second
  701. %%% output trigger and finishing the treatment afterwards. Afterwards a 
  702. %%% full triggering process (two output invocations) is executed again and 
  703. %%% all insertion parts will be accessible in the insertion box.
  704. %%% By the way, the almost same problem appears in \LaTeX{}, too. Almost: in
  705. %%% \LaTeX{} this can happen every time because at the first output invocation
  706. %%% the |\dimen|-values of the footnote insertion is not increased. I leave 
  707. %%% the problem open to the reader\,\dots
  708. %%% \chap Writing the Stuff.
  709. %%% The positions of the bars which mark the changed areas are relative to
  710. %%% the top of the text, i.e.\ the height of the top insertion is not
  711. %%% included.  Therefore it is best to write them just after the top
  712. %%% insertions before the page text---but to do this we have to change the
  713. %%% \beginchange
  714. %%% either the {\sc Plain} {\TeX} macro |\pagecontents| or the {\LaTeX}
  715. %%% macro |\@makecol|.
  716. %%% \endchange
  717. %%% Below is the new definition, I have just rearranged it a little bit so
  718. %%% that it is more legible.  The new lines have been marked with
  719. %%% `|%%%%|'.  |\cb_insert_current_bar| inserts a last element in |\cb_bar_list|
  720. %%% if a changed area is not yet finished, afterwards all bars can be
  721. %%% written.
  722. %%% \beginprog
  723. \def\@makecol{%
  724.    \ifvoid\footins 
  725.      \setbox\@outputbox
  726.        \vbox{
  727.           %\typeout{write all bars -- no footins}     
  728.           \cb_insert_current_bar \cb_write_all_bars %%%%
  729.           \unvbox\@cclv%
  730.             }
  731.    \else
  732.       \setbox\@outputbox
  733.         \vbox{\boxmaxdepth \maxdepth
  734.           %\typeout{write all bars}     
  735.           \cb_insert_current_bar \cb_write_all_bars %%%%
  736.         \unvbox\@cclv\vskip\skip\footins\footnoterule\unvbox\footins%
  737.              }
  738.    \fi%
  739.    \xdef\@freelist{\@freelist\@midlist}\gdef\@midlist{}\@combinefloats
  740.    \setbox\@outputbox\vbox to\@colht{\boxmaxdepth\maxdepth
  741.       \@texttop\dimen128=\dp\@outputbox\unvbox\@outputbox
  742.       \vskip-\dimen128\@textbottom}%
  743.    \global\maxdepth\@maxdepth}
  744. %%% \endprog
  745. %%% \sect If $|\cb_top_change_pos|=|\maxdimen|$ no change is active.  Otherwise
  746. %%% the current change reaches from the begin mark (|\cb_top_change_pos|) to
  747. %%% the end of the page, i.e.\ we insert a virtual end mark.  Because the
  748. %%% change continues on the next page we insert a virtual begin mark on the
  749. %%% top of the page, too.
  750. %%% \beginprog
  751. \def\cb_insert_current_bar{%
  752.    \ifdim \cb_top_change_pos=\maxdimen
  753.    \else%
  754.       \cb_bot_change_pos=\ht\@cclv
  755.       \advance\cb_bot_change_pos by \dp\@cclv
  756.       \xdef\cb_bar_list{\cb_bar_list 
  757.                           \cb_bar(\the\cb_top_change_pos,
  758.                                   \the\cb_bot_change_pos,
  759.                                   \the\changebarwidth)}%
  760.       \global\cb_top_change_pos=0pt
  761.    \fi%
  762. %%% \endprog
  763. %%% \sect Now we can write all bars---if they exist anyway.  It's rather
  764. %%% easy, we just have to define |\cb_bar| to |\cb_write_bar| and execute
  765. %%% |\cb_bar_list|.  The resulting output must not use vertical place.  We must
  766. %%% not forget to delete the list, or we will get the same bars on the next
  767. %%% page again.
  768. %%% \beginprog
  769. \newbox\cb_bars
  770. \newdimen\cb_offset
  771. \def\cb_write_all_bars{%
  772.    \ifx \cb_bar_list\empty
  773.    \else                           % changes exist
  774.       \ifchbarRight
  775.         \cb_offset = \hsize
  776.         \advance \cb_offset by \BarDistance
  777.       \else
  778.         \cb_offset = \hoffset  
  779.         \advance \cb_offset by -\BarDistance
  780.       \fi
  781.       \setbox\cb_bars=\hbox to \cb_offset{%
  782.          \hskip\cb_offset
  783.          \vbox to 0pt{\offinterlineskip
  784.             \let\cb_bar=\cb_write_bar  \cb_bar_list
  785.             }%
  786.          \hss
  787.          }%
  788.       \ht\cb_bars=0pt \dp\cb_bars=0pt \box\cb_bars
  789.       \global\let\cb_bar_list=\empty
  790.    \fi
  791. %%% \endprog
  792. %%% \beginchange\chap Cleaning Up.\endchange
  793. %%% We finish the macro file so that garbage (e.g.\ of exchanges
  794. %%% between systems) can come afterwards.
  795. %%% \beginprog
  796. \catcode`\@=\atcode
  797. \catcode`\_=\uscode
  798. \endinput
  799. %%% \endprog
  800. %%% \end{document}
  801.